use glam::{DVec2, DVec3};

use crate::{ Interval, MaterialRef, AABB };

pub struct Ray {
    /// the start point of the ray
    pub orig: DVec3,
    /// the direction of the ray, normalized
    pub dir: DVec3,
}

impl Ray {
    pub fn new(p: DVec3, dir: DVec3) -> Self {
        Self {
            orig: p,
            dir: dir.normalize(),
        }
    }

    pub fn at(&self, t: f64) -> DVec3 {
        self.orig + self.dir * t
    }
}

pub struct HitRecord {
    /// the distance from the ray origin to the hit point
    pub t: f64,
    /// the texture coordinate of the hit point
    pub uv: DVec2,
    /// the hit point
    pub p: DVec3,
    /// the normal of the hit point
    pub n: DVec3,
    /// whether the ray is hitting the hit point from the front side
    pub front_face: bool,
    /// the material of the hit point
    pub mat: MaterialRef,
}

impl HitRecord {
    pub fn new(t: f64, p: DVec3, mat: MaterialRef) -> Self {

        Self {
            t,
            p,
            uv: DVec2::ZERO,
            n: DVec3::ONE,
            mat,
            front_face: false,
        }
    }

    pub fn set_face_normal(&mut self, ray_dir: &DVec3, outward_normal: &DVec3) {
        self.front_face = ray_dir.dot(*outward_normal) < 0.0;
        self.n = if self.front_face { *outward_normal } else { -*outward_normal };
    }
}

/// hitable trait
/// 
pub trait Hitable {
    /// do ray hit test
    /// 
    fn hit(&self, ray: &Ray, interval: Interval) -> Option<HitRecord>;

    /// the AABB of hit object
    /// 
    fn bounding_box(&self) -> &AABB;
}
